-------------Zykron Hearts-------------
A 4am crack                  2018-07-05
-------------------. updated 2019-03-26
                   |___________________

Name: Zykron Hearts
Genre: games/card
Year: 1986
Publisher: Zykron Company
Platform: Apple ][+ or later
Media: 5.25-inch disk
Sides: 1
OS: DOS 3.3
Previous cracks: none

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  fails on first pass

Locksmith Fast Disk Backup
  unable to read track 4 or 5
  copy boots DOS then crashes

EDD 4 bit copy (no sync, no count)
  no errors, but copy still boots DOS
  then crashes

Copy ][+ nibble editor
  tracks 4 and 5 both look vaguely like
  16-sector tracks, but not quite. But
  when I inspect the half track between
  them, things get even weirder:

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 04.50  START: 263E  LENGTH: 1809
       ^^^^^

27B8: 96 96 96 96 96 96 96 96   VIEW
27C0: 96 96 96 96 96 96 96 96
27C8: 96 96 96 96 96 96 96 96
27D0: 96 96 96 96 96 96 96 96
27D8: 96 96 96 D5 AA AD AA AB <-27DB
               ^^^^^^^^ ^^^^^
               prologue V=001

27E0: AE AB AF AB AB AB DE AA
      ^^^^^ ^^^^^ ^^^^^ ^^^^^
      T=$09 S=$0B chksm epilogue

27E8: EA B5 FE FF FF FF FF D5
27F0: AA AD 96 96 96 96 96 96  FIND:
27F8: 96 96 96 96 96 96 96 96  D5 AA

                 --^--

  Track 4.5 is a lightly modified 16-
  sector track, except the address
  prologue is identical to the data
  prologue ("D5 AA AD"), and according
  to the address field, it's track 9.
  4.5 x 2 = 9, but otherwise it seems
  arbitrary.

Disk Fixer
  looks like a standard DOS 3.3
  track $11 has a standard disk catalog
  T01,S09 -> startup program is "]A"
  followed by some control characters

Why didn't COPYA work?
  tracks 4 and 5 are unreadable because
  the real data is in between

Why didn't Locksmith FDB work?
  some sort of runtime check to ensure
  that track 4.5 has the required data
  and/or structure

Why didn't EDD work?
  Real data being stored on track 4.5.
  Or possibly unused data, but it's
  being checked at runtime. Either way,
  I tested on real hardware to bit copy
  track 4.5 onto a floppy, and the
  copy booted right up. So that half
  track is definitely the key to this
  copy protection.

Next steps:

  1. find routine to read track 4.5
  2. save the data somewhere else (if
     used, otherwise just disable it)
  3. declare victory (*)

(*) go to the gym

                   ~

               Chapter 1
         You Had Me At "]A..."


Booting the disk and attempting to do
anything is... protected.

[S6,D1=non-working copy]

]PR#6
<Ctrl-C>

BREAK
]LIST

PROTECTED

]CATALOG

PROTECTED

]FP

]CATALOG

PROTECTED

The first order of business is removing
the control characters from the file
name and attempting to LIST it from
another disk that isn't so, um,
PROTECTED.

[Disk Fixer]
  [T11,S0F]

                 --v--

-------------- DISK EDIT --------------
TRACK $11/SECTOR $0F/VOLUME $FE/BYTE$0E
---------------------------------------
$00: 00 11 0E 00 00 00 00 00   @QN@@@@@
$08: 00 00 00 12 0F 02 DD C1   @@@ROB]A
                       ^^^^^
$10: 8C 8B 82 A0 A0 A0 A0 A0   ...
     ^^^^^^^^
  I'll change it to "HELLO"

$18: A0 A0 A0 A0 A0 A0 A0 A0
$20: A0 A0 A0 A0 A0 A0 A0 A0
$28: A0 A0 A0 A0 02 00 12 0D       B@RM
$30: 02 DD C2 8C 8B 82 A0 A0   B]B...
$38: A0 A0 A0 A0 A0 A0 A0 A0
$40: A0 A0 A0 A0 A0 A0 A0 A0

                 --^--

[S6,D1=non-working copy]
[S5,D1=my work disk]

]PR#5
...

]CATALOG,S6

C1983 DSR^C#254
063 FREE

 A 002 HELLO
 A >59 ]B
 A 051 ]C
 B 047 ]D
 B 010 ]E.BSHP
 B 034 ]F1
 B 034 ]F2
 B 034 ]F3
 B 034 ]F4
 B 034 ]F5
 B 002 ]G.SLIB
 A 011 ]H
 A 006 ]I
 A 014 ]J
 A 005 ]K
 B 004 ]L
*T 002 ]M
*T 002 ]N
 T 002 ]O
 B 002 ]P
 A >60 DEMO

]LOAD HELLO
]LIST

 0  REM  COPYRIGHT 1986 ZYKRON CO
     .PR#6
 10 C$ =  CHR$ (4)
 15  CALL 45995
 20  POKE 50,128
 21  PRINT C$;"BLOAD ]P"
 22  POKE 2,9: POKE 47100,0: CALL
     16384: POKE 47100,1
 23  POKE 1012,0
 24  IF  PEEK (46989) <  > 173 THEN
      CALL 49282
 25  PRINT C$;"BRUN ]L"
 30  PRINT C$;"RUN ]I"

I'm pretty sure there was supposed to
be an Applesoft trick on line 0 where
it puts a Ctrl-D character at the start
of a new line and DOS interprets it as
a command. (YES CHILDREN, GATHER 'ROUND
as I tell you the tale of the early
computers, where listing a program
could execute arbitrary DOS commands.)

But they missed, so it doesn't reboot
from the misaligned "PR#6" command and
just lists it instead.

Anyway.

45995 is $B3AB. What's at $B3AB? That's
part of the disk volume name (usually
"DISK VOLUME", but things like Diversi-
DOS and Pronto-DOS had their own titles
in there). At any rate, it's not
executable code.

Except on this disk, where it is.

Returning to my trusty Disk Fixer
sector editor, I can see what ends up
at $B3AB by reading track 2, sector 2.

                 --v--

-------------- DISK EDIT --------------
TRACK $02/SECTOR $02/VOLUME $FE/BYTE$AB
---------------------------------------
$80: C5 B5 18 90 01 38 08 8D   E5X.A8H.
$88: C5 B5 A9 00 85 48 20 7E   E5)@.H >
$90: AE 28 AE 9B B3 9A 60 11   .(..3. Q
$98: 0F 00 00 EE 00 01 00 00   O@@n@A@@
$A0: 00 00 FF FF 01 0A 64 A0   @@..AJ$
$A8: A0 A0 A0 60 D2 76 78 79       R689
              ^^

$B0: 71 60 54 48 47 49 52 59   1 THGIRY
$B8: 50 4F 43 04 11 0F 03 00   POCDQOC@

                 --^--

On this disk, $B3AB is an RTS. So the
CALL on line 15 does nothing, except
of course verify that you booted from
the original disk's "PROTECTED" DOS.

That's great. DRM is great.

                   ~

               Chapter 2
 Double Your Pleasure, Double Your Fun


Line 22 is more interesting. We've
BLOADed a file (the name looks normal
but it, too, is riddled with control
character) and now we're calling 16384,
which is $4000. If I delete line 15 and
line 20, then put an "END" statement on
line 22, I can let the program BLOAD
the file for me and return control.

]15
]20
]22 END
]RUN

]CALL -151

*4000L

; get address of RWTS parameter table
4000-   20 E3 03    JSR   $03E3

; stash it in $00/$01
4003-   84 00       STY   $00
4005-   85 01       STA   $01

; $02 was set to 9 in the BASIC program
; (line 22)
4007-   A5 02       LDA   $02

; set that as the track
4009-   A0 04       LDY   #$04
400B-   91 00       STA   ($00),Y

; RWTS command = 0 (seek)
400D-   A9 00       LDA   #$00
400F-   A0 0C       LDY   #$0C
4011-   91 00       STA   ($00),Y

; volume 0 (wildcard)
4013-   A9 00       LDA   #$00
4015-   A0 03       LDY   #$03
4017-   91 00       STA   ($00),Y

; execute the seek
4019-   20 E3 03    JSR   $03E3
401C-   20 D9 03    JSR   $03D9

; reset DOS
401F-   A9 00       LDA   #$00
4021-   85 48       STA   $48

; get the slot number
4023-   A0 01       LDY   #$01
4025-   B1 00       LDA   ($00),Y
4027-   AA          TAX

; turn on drive motor manually (after
; the RWTS call turned it off on the
; way out)
4028-   BD 89 C0    LDA   $C089,X
402B-   BD 8E C0    LDA   $C08E,X

; ($00) -> $1000
402E-   A9 00       LDA   #$00
4030-   85 00       STA   $00
4032-   A9 10       LDA   #$10
4034-   85 01       STA   $01

; find an $FF nibble
4036-   A0 00       LDY   #$00
4038-   BD 8C C0    LDA   $C08C,X
403B-   10 FB       BPL   $4038
403D-   C9 FF       CMP   #$FF
403F-   D0 F7       BNE   $4038

; and another (or start over)
4041-   BD 8C C0    LDA   $C08C,X
4044-   10 FB       BPL   $4041
4046-   C9 FF       CMP   #$FF
4048-   D0 EE       BNE   $4038

; find a non-$FF nibble
404A-   BD 8C C0    LDA   $C08C,X
404D-   10 FB       BPL   $404A
404F-   C9 FF       CMP   #$FF
4051-   F0 F7       BEQ   $404A
4053-   D0 05       BNE   $405A

; store raw disk nibbles at $1000+
4055-   BD 8C C0    LDA   $C08C,X
4058-   10 FB       BPL   $4055
405A-   91 00       STA   ($00),Y
405C-   E6 00       INC   $00
405E-   D0 F5       BNE   $4055
4060-   E6 01       INC   $01
4062-   A5 01       LDA   $01

; until $4000
4064-   C9 40       CMP   #$40
4066-   90 ED       BCC   $4055

; turn off drive motor
4068-   BD 88 C0    LDA   $C088,X

; copy the third nibble
406B-   AD 02 10    LDA   $1002
406E-   8D 8D B7    STA   $B78D
4071-   60          RTS

And that's... it? I'm confused. What
does track 9 have to do with anything?

                   ~

               Chapter 3
       Don't Mess With The Zohan


If I missed something, it was part of
the BASIC program that got us here.
Here, again, is line 22 that called the
program we called at $4000:

 22  POKE 2,9: POKE 47100,0: CALL
     16384: POKE 47100,1

I know how address $02 is used -- it
ends up as the track number in the seek
we're doing (read at $4007). But there
are two other POKEs here, surrounding
the CALL like bookends: 47100.

47100is $B7FC. We set it to 0, then
call this protection routine, then set
it (back?) to 1. What's $B7FC?
According to the indispensible "Beneath
Apple DOS" (p. 8-35), it's part of the
DCT. We're messing with the DCT. Nobody
messes with the DCT.

DCT ("Device Characteristics Table")
starts at $B7FB, is only four bytes
long, and should never, ever change:

  DCT+0 - device type (should be $00)
  DCT+1 - phases per track (should be
          $01)
  DCT+2 - motor on time count (should
          be $EF, $D8)

But we are changing it, specifically
the "phases per track" field at $B7FC,
from #$01 to #$00. I don't know what
effect this has, because literally no
one ever does this.

One phase is half a track. One track is
two phases. These are just definitions.
If you want to seek to track N, you
tell the RWTS to seek to phase N*2. All
disks work this way.

Except this one.

I pored through memory until I found
where the DCT is used. Here, shortly
after the RWTS entry point at $BD00, we
get the DCT address from the RWTS
parameter table:

BD42-   A0 06       LDY   #$06
BD44-   B1 48       LDA   ($48),Y
BD46-   99 36 00    STA   $0036,Y
BD49-   C8          INY
BD4A-   C0 0A       CPY   #$0A
BD4C-   D0 F6       BNE   $BD44

Among other things, that will set up
($3C) to point to the DCT, because it's
taking RWTS+6 and putting it in $36+6.

Then, when we try to switch to a
different track, we see this code:

*BE3BL

; accumulator has the desired track at
; this point ($00-$22 on a normal disk)
BE3B-   48          PHA

; get DCT+1 (documented as "phases per
; track")
BE3C-   A0 01       LDY   #$01
BE3E-   B1 3C       LDA   ($3C),Y

; look at bit 0
BE40-   6A          ROR
BE41-   68          PLA

; if bit 0 is 0, skip ahead to seek
BE42-   90 08       BCC   $BE4C

; otherwise, multiply the desired track
; by 2 (now $00-$44)
BE44-   0A          ASL

; now seek
BE45-   20 4C BE    JSR   $BE4C
BE48-   4E 78 04    LSR   $0478
BE4B-   60          RTS

Then $BE4C initializes the drive motor
and seeks to the phase it was given. So
there are really only two code paths,
one that moves two phases per track,
and another that moves one phase per
track. By changing DCT+1 from #$01 to
#$00, we've told the drive seek routine
to treat its input as a phase, not a
track. We're passing the accumulator
directly to the drive seek routine at
$BE4C, without ever multiplying it by 2
(with the "ASL" at $BE44).

DCT+1 isn't really "phases per track."
It's a boolean, either 0 or 1.

  1 - seek by track (default)
  0 - seek by phase

So we're seeking to track 4.5.

Boom.

                   ~

               Chapter 4
    In Which Our Adventure Comes To
        A Satisfying Conclusion


As usual, bypassing the copy protection
is orders of magnitude easier than
understanding it. The assembly routine
at $4000 reads disk nibbles from track
4.5 and copies the third one to $B78D.
Then the BASIC program that called it
reads that value and checks if it's 173
(#$AD). I can change the routine to
unconditionally set that address to the
correct value.

[Disk Fixer]
  ["D"irectory mode]
    [Select "]P" file]

...takes me to T0A,S03

T0A,S03,$04 -> A9 AD 8D 8D B7 60

...which looks like this at runtime:

*4000L

4000-   A9 AD       LDA   #$AD
4002-   8D 8D B7    STA   $B78D
4005-   60          RTS

Quod erat liberandum.

                   ~

               Changelog

2019-03-26

- fix minor corruption in two graphics
  files

2018-07-05

- initial release

---------------------------------------
A 4am crack                    No. 1770
------------------EOF------------------
